﻿using Microsoft.EntityFrameworkCore;
using Sgr.Domain.Entities.Auditing;
using Sgr.Domain.Repositories;
using Sgr.EntityFrameworkCore;
using Sgr.Exceptions;
using Sgr.Generator;
using Sgr.UPMS.Domain.Users;

namespace Sgr.UPMS.Infrastructure.Repositories
{
    /// <summary>
    /// 用户仓储实现
    /// </summary>
    /// <remarks>
    /// 实现了用户相关的数据访问操作：
    /// - 用户的基础CRUD操作
    /// - 用户关系查询（部门、职务、角色等）
    /// - 用户名唯一性检查
    /// </remarks>
    public class UserRepository<TDbContext>
        : EfCoreRepositoryOfTEntityAndTPrimaryKey<TDbContext, User, long>, IUserRepository where TDbContext : UnitOfWorkDbContext
    {
        private readonly ISegmentNumIdGenerator _numberIdGenerator;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbContextProvider"></param>
        /// <param name="auditedOperator">审计操作者</param>
        /// <param name="numberIdGenerator">数字ID生成器</param>
        /// <exception cref="BusinessException">参数为空时抛出异常</exception>
        public UserRepository(
            IDbContextProvider<TDbContext> dbContextProvider,
            IAuditedOperator auditedOperator,
            ISegmentNumIdGenerator numberIdGenerator)
            : base(dbContextProvider, auditedOperator)
        {
            _numberIdGenerator = numberIdGenerator ?? throw new ArgumentNullException(nameof(numberIdGenerator));
        }

        /// <summary>
        /// 检查并设置主键Id
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        protected override async Task CheckAndSetIdAsync(User entity, CancellationToken cancellationToken = default)
        {
            if (entity.Id == 0)
                entity.Id = await _numberIdGenerator.GetUniqueIdAsync(UpmsConstant.USER_SEGMENT_KEY, cancellationToken);
        }

        /// <summary>
        /// 检查登录名是否唯一
        /// </summary>
        /// <param name="loginName">登录名</param>
        /// <param name="excludedId">需要排除的用户ID（用于编辑场景）</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>true: 登录名唯一; false: 登录名已存在</returns>
        /// <exception cref="ArgumentException">当loginName为空时抛出</exception>
        public async Task<bool> LoginNameIsUniqueAsync(
            string loginName,
            long? excludedId = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(loginName, nameof(loginName));

            var dbContext = await GetDbContextAsync(cancellationToken);
            var query = dbContext.Set<User>()
                .AsNoTracking()
                .Where(x => x.LoginName == loginName);

            if (excludedId.HasValue && excludedId.Value > 0)
            {
                query = query.Where(x => x.Id != excludedId.Value);
            }

            return !await query.AnyAsync(cancellationToken);
        }

        /// <summary>
        /// 检查手机号码是否唯一
        /// </summary>
        /// <param name="phoneNumber"></param>
        /// <param name="excludedId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<bool> PhoneNumberIsUniqueAsync(
            string phoneNumber,
            long? excludedId = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(phoneNumber, nameof(phoneNumber));

            var dbContext = await GetDbContextAsync(cancellationToken);
            var query = dbContext.Set<User>()
                .AsNoTracking()
                .Where(x => x.UserPhone == phoneNumber);

            if (excludedId.HasValue && excludedId.Value > 0)
            {
                query = query.Where(x => x.Id != excludedId.Value);
            }

            return !await query.AnyAsync(cancellationToken);
        }

        /// <summary>
        /// 检查手机号码是否唯一
        /// </summary>
        /// <param name="email"></param>
        /// <param name="excludedId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<bool> EmailIsUniqueAsync(
            string email,
            long? excludedId = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(email, nameof(email));

            string normalizedEmail = email.Trim().ToLowerInvariant();

            var dbContext = await GetDbContextAsync(cancellationToken);
            var query = dbContext.Set<User>()
                .AsNoTracking()
                .Where(x => x.UserEmail == normalizedEmail);

            if (excludedId.HasValue && excludedId.Value > 0)
            {
                query = query.Where(x => x.Id != excludedId.Value);
            }

            return !await query.AnyAsync(cancellationToken);
        }

        /// <summary>
        /// 检查微信openId是否唯一
        /// </summary>
        /// <param name="weChatOpenId"></param>
        /// <param name="excludedId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<bool> WeChatOpenIdIsUniqueAsync(
            string weChatOpenId,
            long? excludedId = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(weChatOpenId, nameof(weChatOpenId));

            var dbContext = await GetDbContextAsync(cancellationToken);
            var query = dbContext.Set<User>()
                .AsNoTracking()
                .Where(x => x.WeChatOpenId == weChatOpenId);

            if (excludedId.HasValue && excludedId.Value > 0)
            {
                query = query.Where(x => x.Id != excludedId.Value);
            }

            return !await query.AnyAsync(cancellationToken);
        }

        /// <summary>
        /// 检查微信unionId是否唯一
        /// </summary>
        /// <param name="weChatUnionId"></param>
        /// <param name="excludedId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<bool> WeChatUnionIdIsUniqueAsync(
            string weChatUnionId,
            long? excludedId = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(weChatUnionId, nameof(weChatUnionId));

            var dbContext = await GetDbContextAsync(cancellationToken);
            var query = dbContext.Set<User>()
                .AsNoTracking()
                .Where(x => x.WeChatUnionId == weChatUnionId);

            if (excludedId.HasValue && excludedId.Value > 0)
            {
                query = query.Where(x => x.Id != excludedId.Value);
            }

            return !await query.AnyAsync(cancellationToken);
        }

        /// <summary>
        /// 根据登录名获取用户
        /// </summary>
        /// <param name="loginName">登录名</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户实体，不存在返回null</returns>
        /// <exception cref="ArgumentException">当loginName为空时抛出</exception>
        public async Task<User?> GetByLoginNameAsync(
            string loginName,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(loginName, nameof(loginName));

            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .FirstOrDefaultAsync(f => f.LoginName == loginName, cancellationToken);
        }

        /// <summary>
        /// 根据登录名获取用户（包含导航属性）
        /// </summary>
        /// <param name="loginName">登录名</param>
        /// <param name="propertiesToInclude">要包含的导航属性</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户实体，不存在返回null</returns>
        public async Task<User?> GetByLoginNameAsync(
            string loginName,
            string[] propertiesToInclude,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(loginName, nameof(loginName));

            var query = await GetQueryableAsync(propertiesToInclude, cancellationToken);
            return await query
                .FirstOrDefaultAsync(f => f.LoginName == loginName, cancellationToken);
        }

        /// <summary>
        /// 根据邮箱名称获取用户
        /// </summary>
        /// <param name="email">邮箱</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户实体，不存在返回null</returns>
        public async Task<User?> GetByEmailAsync(
            string email,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(email, nameof(email));

            string normalizedEmail = email.Trim().ToLowerInvariant();

            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .FirstOrDefaultAsync(f => f.UserEmail == normalizedEmail, cancellationToken);
        }

        /// <summary>
        /// 根据手机号码获取用户
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户实体，不存在返回null</returns>
        public async Task<User?> GetByPhoneAsync(
            string phone,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrEmpty(phone, nameof(phone));

            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .FirstOrDefaultAsync(f => f.UserPhone == phone, cancellationToken);
        }

        /// <summary>
        /// 获取指定部门的所有用户
        /// </summary>
        /// <param name="departmentId">部门ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户列表</returns>
        public async Task<IEnumerable<User>> GetByDepartmentIdAsync(
            long departmentId,
            CancellationToken cancellationToken = default)
        {
            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .Where(f => f.DepartmentId == departmentId)
                .ToArrayAsync(cancellationToken);
        }

        /// <summary>
        /// 获取指定组织的所有用户
        /// </summary>
        /// <param name="orgId">组织ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户列表</returns>
        public async Task<IEnumerable<User>> GetByOrgIdAsync(
            long orgId,
            CancellationToken cancellationToken = default)
        {
            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .Where(f => f.OrgId == orgId)
                .ToArrayAsync(cancellationToken);
        }

        /// <summary>
        /// 获取指定职务的所有用户
        /// </summary>
        /// <param name="dutyId">职务ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户列表</returns>
        public async Task<IEnumerable<User>> GetByDutyIdAsync(
            long dutyId,
            CancellationToken cancellationToken = default)
        {
            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .Where(u => u.Duties.Any(d => d.DutyId == dutyId))
                .ToArrayAsync(cancellationToken);
        }

        /// <summary>
        /// 获取首要职务为指定职务的所有用户
        /// </summary>
        /// <param name="primaryDutyId">首要职务ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户列表</returns>
        public async Task<IEnumerable<User>> GetByPrimaryDutyIdAsync(
            long primaryDutyId,
            CancellationToken cancellationToken = default)
        {
            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .Where(f => f.PrimaryDutyId == primaryDutyId)
                .ToArrayAsync(cancellationToken);
        }

        /// <summary>
        /// 检查角色是否被用户使用
        /// </summary>
        /// <param name="roleId">角色ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>true: 角色被使用; false: 角色未使用</returns>
        public async Task<bool> HasUserUseRoleAsync(
            long roleId,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync(cancellationToken);
            return await dbContext.Set<User>()
                .AsNoTracking()
                .AnyAsync(u => u.Roles.Any(r => r.RoleId == roleId), cancellationToken);
        }

        /// <summary>
        /// 获取拥有指定角色的所有用户
        /// </summary>
        /// <param name="roleId">角色ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户列表</returns>
        public async Task<IEnumerable<User>> GetByRoleIdAsync(
            long roleId,
            CancellationToken cancellationToken = default)
        {
            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .Where(u => u.Roles.Any(d => d.RoleId == roleId))
                .ToArrayAsync(cancellationToken);
        }

        public async Task<IEnumerable<(long Id, long OrgId)>> GetUserIdAndOrgIdByRoleIdAsync(
            long roleId,
            CancellationToken cancellationToken = default)
        {
            var query = await GetQueryableAsync(cancellationToken);
            return await query
                .Where(u => u.Roles.Any(d => d.RoleId == roleId))
                .Select(u => new ValueTuple<long, long>(u.Id, u.OrgId))
                .ToArrayAsync(cancellationToken);
        }
    }
}